/**
 * @file       st7789.c
 * @author     wangbing
 * @email      mz8023yt@163.com
 * @version    V1.0
 * @date       2022-03-08
 * @brief      ips lcd panel drvier
 */

#include "st7789.h"

void st7789_swspi_write(struct st7789* lcd, uint8_t data)
{
        uint8_t i;
        for (i = 0; i < 8; i++) {
                lcd->ops.pin.clk(LOW);
                if (data & 0x80)
                        lcd->ops.pin.mo(HIGH);
                else
                        lcd->ops.pin.mo(LOW);
                lcd->ops.pin.clk(HIGH);
                data <<= 1;
        }
}

void st7789_reg_write(struct st7789* lcd, uint8_t addr)
{
        lcd->ops.pin.cs(LOW);

        /* first bit of 9bits, zore is command */
        lcd->ops.pin.clk(LOW);
        lcd->ops.pin.mo(LOW);
        lcd->ops.pin.clk(HIGH);

        /* next 8bits of 9bits, data value */
        st7789_swspi_write(lcd, addr);

        lcd->ops.pin.cs(HIGH);
}

void st7789_data_write(struct st7789* lcd, uint8_t data)
{
        lcd->ops.pin.cs(LOW);

        /* first bit of 9bits, one is data */
        lcd->ops.pin.clk(LOW);
        lcd->ops.pin.mo(HIGH);
        lcd->ops.pin.clk(HIGH);

        /* next 8bits of 9bits, register address */
        st7789_swspi_write(lcd, data);

        lcd->ops.pin.cs(HIGH);
}

void st7789_16bit_write(struct st7789* lcd, uint16_t data)
{
        lcd->ops.pin.cs(LOW);

        /* first bit of 9bits, one is data */
        lcd->ops.pin.clk(LOW);
        lcd->ops.pin.mo(HIGH);
        lcd->ops.pin.clk(HIGH);

        /* next 8bits of 9bits, register address */
        st7789_swspi_write(lcd, data >> 8);

        /* first bit of 9bits, one is data */
        lcd->ops.pin.clk(LOW);
        lcd->ops.pin.mo(HIGH);
        lcd->ops.pin.clk(HIGH);

        /* next 8bits of 9bits, register address */
        st7789_swspi_write(lcd, data);

        lcd->ops.pin.cs(HIGH);
}

void st7789_init(struct st7789* lcd)
{
        /* reset sequence */
        lcd->ops.pin.rst(LOW);
        lcd->ops.time.msleep(20);
        lcd->ops.pin.rst(HIGH);
        lcd->ops.time.msleep(20);

        /* display and color format setting */
        st7789_reg_write(lcd, 0x36);
        if (0 == lcd->param.var.orien)
                st7789_data_write(lcd, 0x00);
        else if (1 == lcd->param.var.orien)
                st7789_data_write(lcd, 0xC0);
        else if (2 == lcd->param.var.orien)
                st7789_data_write(lcd, 0x70);
        else
                st7789_data_write(lcd, 0xA0);

        /* pixel inversion on/off, 20h off, 21h on */
        st7789_reg_write(lcd, 0x21);

        st7789_reg_write(lcd, 0x3a);
        st7789_data_write(lcd, 0x05);

        /* frame rate setting */
        st7789_reg_write(lcd, 0xb2);
        st7789_data_write(lcd, 0x0c);
        st7789_data_write(lcd, 0x0c);
        st7789_data_write(lcd, 0x00);
        st7789_data_write(lcd, 0x33);
        st7789_data_write(lcd, 0x33);

        st7789_reg_write(lcd, 0xb7);
        st7789_data_write(lcd, 0x35);

        /* power setting */
        st7789_reg_write(lcd, 0xbb);
        st7789_data_write(lcd, 0x1f);

        st7789_reg_write(lcd, 0xc0);
        st7789_data_write(lcd, 0x2c);

        st7789_reg_write(lcd, 0xc2);
        st7789_data_write(lcd, 0x01);

        st7789_reg_write(lcd, 0xc3);
        st7789_data_write(lcd, 0x1d);

        st7789_reg_write(lcd, 0xc4);
        st7789_data_write(lcd, 0x20);

        st7789_reg_write(lcd, 0xc6);
        st7789_data_write(lcd, 0x0f);

        st7789_reg_write(lcd, 0xd0);
        st7789_data_write(lcd, 0xa4);
        st7789_data_write(lcd, 0xa1);

        st7789_reg_write(lcd, 0xE4);
        st7789_data_write(lcd, 0x27);
        st7789_data_write(lcd, 0x00);
        st7789_data_write(lcd, 0x10);

        /* gamma setting */
        st7789_reg_write(lcd, 0xe0);
        st7789_data_write(lcd, 0xd0);
        st7789_data_write(lcd, 0x00);
        st7789_data_write(lcd, 0x14);
        st7789_data_write(lcd, 0x15);
        st7789_data_write(lcd, 0x13);
        st7789_data_write(lcd, 0x2c);
        st7789_data_write(lcd, 0x42);
        st7789_data_write(lcd, 0x43);
        st7789_data_write(lcd, 0x4e);
        st7789_data_write(lcd, 0x09);
        st7789_data_write(lcd, 0x16);
        st7789_data_write(lcd, 0x14);
        st7789_data_write(lcd, 0x18);
        st7789_data_write(lcd, 0x21);

        st7789_reg_write(lcd, 0xe1);
        st7789_data_write(lcd, 0xd0);
        st7789_data_write(lcd, 0x00);
        st7789_data_write(lcd, 0x14);
        st7789_data_write(lcd, 0x15);
        st7789_data_write(lcd, 0x13);
        st7789_data_write(lcd, 0x0b);
        st7789_data_write(lcd, 0x43);
        st7789_data_write(lcd, 0x55);
        st7789_data_write(lcd, 0x53);
        st7789_data_write(lcd, 0x0c);
        st7789_data_write(lcd, 0x17);
        st7789_data_write(lcd, 0x14);
        st7789_data_write(lcd, 0x23);
        st7789_data_write(lcd, 0x20);

        /* exit sleep mode */
        st7789_reg_write(lcd, 0x11);
        lcd->ops.time.msleep(120);

        /* display on */
        st7789_reg_write(lcd, 0x29);

        lcd->ops.pin.bl(HIGH);
}

void st7789_area_set(struct st7789* lcd, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
{
        if (0 == lcd->param.var.orien || 2 == lcd->param.var.orien) {
                y1 = y1 + lcd->param.fix.yoffset;
                y2 = y2 + lcd->param.fix.yoffset;
                st7789_reg_write(lcd, 0x2a);
                st7789_16bit_write(lcd, x1);
                st7789_16bit_write(lcd, x2);
                st7789_reg_write(lcd, 0x2b);
                st7789_16bit_write(lcd, y1);
                st7789_16bit_write(lcd, y2);
                st7789_reg_write(lcd, 0x2c);
        } else {
                y1 = y1 + lcd->param.fix.yoffset;
                y2 = y2 + lcd->param.fix.yoffset;
                st7789_reg_write(lcd, 0x2a);
                st7789_16bit_write(lcd, x1);
                st7789_16bit_write(lcd, x2);
                st7789_reg_write(lcd, 0x2b);
                st7789_16bit_write(lcd, y1);
                st7789_16bit_write(lcd, y2);
                st7789_reg_write(lcd, 0x2c);
        }
}

void st7789_point_draw(struct st7789* lcd, uint16_t x, uint16_t y, uint16_t color)
{
        st7789_area_set(lcd, x, y, x, y);
        st7789_16bit_write(lcd, color);
}

void st7789_rect_fill(struct st7789* lcd, uint16_t xsta, uint16_t ysta, uint16_t xend, uint16_t yend, uint16_t color)
{
        uint16_t i, j;
        st7789_area_set(lcd, xsta, ysta, xend, yend);
        for (i = ysta; i <= yend; i++) {
                for (j = xsta; j <= xend; j++)
                        st7789_16bit_write(lcd, color);
        }
}

void st7789_screen_fill(struct st7789* lcd, uint16_t color)
{
        uint16_t i, j;
        st7789_area_set(lcd, 0, 0, lcd->param.fix.width - 1, lcd->param.fix.height - 1);
        for (i = 0; i < lcd->param.fix.width; i++) {
                for (j = 0; j < lcd->param.fix.height; j++) {
                        st7789_16bit_write(lcd, color);
                }
        }
}
